package GA;

//File: EA.java
//Author: John Hallam, based on integer EA by Peter Ross,
//    built upon NU's basic skeleton
//
//This file contains a simple real vector GA.
//
//It deliberately does *not* make use of GUI components; it is intended
//to be run from a command-line prompt. For example, to compile it when
//you have adapted it to your needs:
//javac EA.java
//and to run it, with suitable arguments:
//java  EA 2317 500
//that is, seed the random generator with the number 2317, and run for
//500 iterations of the main loop in run(...).  If you run it with no
//arguments you get a very little help.
//
//Why is there no GUI, you ask? To keep it simple. Experience suggests
//that, in programs that have GUIs, a lot more than half the code is
//concerned with the GUI rather than with the core activity.

import java.io.*;
import java.util.*;

public class EA 
{
	// ======== Class variables ============
	
	int POP_SIZE = 100;		// the population size
	int CHROMOSOME_SIZE = 16;	// the chromosome size
	
	// Variables to do with the EA itself:
	// -- the population, each member (chromosome) is one row of this array:
	float pop[][] = new float[POP_SIZE][CHROMOSOME_SIZE];
	// -- the fitness of each member;  to be maximised:
	float fitness[] = new float[POP_SIZE];
	// -- the best fitness in the population (used for reporting)
	float bestPopFitness;
	
	// Random number generator, to be initialised in the constructor:
	Random r;
	
	// ======== Constructor(s) ============
	
	// A basic constructor for class EA. Takes a random number generator
	// seed as the argument.
	
	public EA(long seed)
	{
		r = new Random(seed); // Create a new random generator, suitably seeded
		initPop();		  // Initialise population
	}
	
	// ======== Class methods ========
	
	// Do tournament selection of given size, return index of chosen member
	public int tournament(int tSize) 
	{
		int i, j;
		float bestFit = 0;
		int best = 0;
		
		for(i=0; i<tSize; i++) 
		{
			j = r.nextInt(POP_SIZE);
			if(fitness[j] > bestFit) 
			{
				 bestFit = fitness[j];
				 best = j;
			}
		}
		return(best);
	}
	
	// Crossover operator (single point)
	public void crossover(int c1, int c2, float [] kid) 
	{
		int i, cut1;
		
		// a random number in 1..CHROMOSOME_SIZE-1 inclusive
		cut1 = 1 + r.nextInt(CHROMOSOME_SIZE-1);
		
		for(i=0; i<cut1; i++)
			kid[i] = pop[c1][i];
		for(i=cut1; i<CHROMOSOME_SIZE; i++)
			kid[i] = pop[c2][i];
	}
	
	// utation operator
	public void mutate(float [] kid) 
	{
		for(int i=0; i<CHROMOSOME_SIZE; i++)
			if(r.nextInt(CHROMOSOME_SIZE) == 1)
				kid[i] = r.nextFloat();
	}
	
	// Insertion operator -- replace the worst, if better
	public void insert(float [] kid, float val) 
	{
		int worst;
		float worstFit;
		
		// Find the worst member of the current population
		worstFit = fitness[0]; worst=0;
		for(int i=0; i<POP_SIZE; i++) 
		{
			if(worstFit > fitness[i]) 
			{
				worst = i;
				worstFit = fitness[i];
			}
		}
		// kid replaces it if kid is better
		if(val > worstFit) 
		{
			for(int i=0; i<CHROMOSOME_SIZE; i++) 
				pop[worst][i] = kid[i];
			
			fitness[worst] = val;
		}
	}
	
	// Fitness evaluation -- simple sum
	public float evaluate(float [] chromosome) 
	{
		float value = 0;
		
		for(int i=0; i<CHROMOSOME_SIZE; i++)
			value += chromosome[i];
		
		return value;
	}
	
	// Run the GA.
	public void run(int gensLimit)
	{
		int c1, c2;
		float[] kid = new float[CHROMOSOME_SIZE];
		float val;
		
		for(int i=0; i<gensLimit; i++)
		{
			c1 = tournament(2);
			c2 = tournament(2);
			crossover(c1, c2, kid);
			mutate(kid);
			val = evaluate(kid);
			if(val > bestPopFitness)
				bestPopFitness = val;
			
			insert(kid, val);
			if(i % 100 == 0)
				System.out.println("# " + i + " " + bestPopFitness);
		}
	}
	
	// Produce final result of run by writing result and best fitness to screen.
	public void reportResult() 
	{
		int best;
		float bestFit;
		
		bestFit = 0;		// Fitness of winning chromosome
		best = 0;			// Index of wiining chromosome
		
		for(int i=0; i<POP_SIZE; i++) 
		{
			if(fitness[i] > bestFit)
			{
				 bestFit = fitness[i];
				 best = i;
			}
		}
		for(int i=0; i<CHROMOSOME_SIZE; i++)
			System.out.println(i + " " + pop[best][i]);
		System.out.println("# Best Value = " + bestFit + " at " + best);
	}
	
	// ethod to initialise the population. 
	
	public void initPop()
	{
		// Randomly initialise chromosomes
		for(int c = 0; c < POP_SIZE; c++)
		{
			for(int g = 0; g < CHROMOSOME_SIZE; g++)
			{
				pop[c][g] = r.nextFloat();
			}
			fitness[c] = evaluate(pop[c]);
		}
	}
	
	// The entry point of the whole thing:
	public static void main(String[] args) 
	{
		long seed;             // For the random number generator
		int generations = 100; // How many generations to run for
		
		// Zero or one arguments? Give a little help!
		if(args.length < 1) 
		{
			System.out.print("needs a seed value, ");
			System.out.println("and maybe a generations limit");
			System.exit(0);
		}
		seed = Long.parseLong(args[0]);
		if(args.length > 1) 
		{
			generations = Integer.parseInt(args[1]);
		}
		
		// Create the EA, run it, report the final outcome:
		EA myEA = new EA(seed);
		myEA.run(generations);
		myEA.reportResult();
	}

} // end of class EA